#include "Sniffer.h"
#include "Broady.h"
#include "Application.h"
#include "External.h"

namespace Broady {

bool Sniffer::Init( PCap::DevicePtr dev )
{
	if( !dev )
	{
		return false;
	}

	if( dev->m_addresses.empty( ) || !dev->m_addresses[0].m_addr )
	{
		OutputMessage( "Unable to determine the address of the sniffing device.\n" );

		return false;
	}

	memcpy( &m_ip, &( *dev->m_addresses[0].m_addr ), sizeof( m_ip ) );

	m_handle = PCap::Open( dev );

	if( !m_handle )
	{
		return false;
	}

	if( !m_handle->SetNonBlock( false ) )
	{
		OutputMessage( "Unable to activate non-blocking mode.\n" );
	}

	return true;
}

void Sniffer::Quit( void )
{
	// Clear reference to the pointer, so boost can destroy it
	m_handle = boost::shared_ptr<PCap>( );
}

bool Sniffer::Step( void )
{
	if( !m_handle->Dispatch( -1 /* all packets */, reinterpret_cast<PCap::HandlerProc>( PacketHandler ), this ) )
	{
		return false;
	}

	return true;
}

bool Sniffer::FilterPacket( ip_header* ih, udp_header* uh ) const
{
	unsigned long daddr = 0xFFFFFFFF;

	if( memcmp( &m_ip, &ih->saddr, 4 ) != 0 )
	{
		return false;
	}

	if( memcmp( &ih->daddr, &daddr, 4 ) != 0 )
	{
		return false;
	}

	if( g_App->GetExternal( )->IsLocalNode( htons( uh->sport ) ) )
	{
		return false;
	}

	return true;
}

void Sniffer::PacketHandler( unsigned char* param, const struct pcap_pkthdr* header, const unsigned char* pkt_data )
{
	Sniffer* _this = reinterpret_cast<Sniffer*>( param );

	if( !_this )
	{
		return;
	}

	ip_header* ih;
	udp_header* uh;
	u_int ip_len;

	ih = ( ip_header* )( pkt_data + 14 );
	ip_len = ( ih->ver_ihl & 0xF ) * 4;
	uh = ( udp_header* )( ( unsigned char* ) ih + ip_len );

	if( !_this->FilterPacket( ih, uh ) )
	{
		return;
	}

	/*sport = ntohs( uh->sport );
	dport = ntohs( uh->dport );*/

	/*printf( "%d.%d.%d.%d:%d -> %d.%d.%d.%d:%d\n",
		ih->saddr.byte1,
		ih->saddr.byte2,
		ih->saddr.byte3,
		ih->saddr.byte4,
		sport,
		ih->daddr.byte1,
		ih->daddr.byte2,
		ih->daddr.byte3,
		ih->daddr.byte4,
		dport );*/

	g_App->GetExternal( )->Incoming( pkt_data, header->len );
}

} // namespace
